using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Configuration;
using System.IO;
using Crab.DataModel;
using Crab.DataModel.Common;
using Crab.DataModel.Utility;

namespace ClassEditor
{
    public partial class ClassEditor : Form
    {
        private TreeNode curTreeNode = null;
        private TreeNode _classesNode = null;
        private DirtyFlags _dirtyFlag;

        StringBuilder outputResult = new StringBuilder();
       
        public ClassEditor()
        {
            InitializeComponent();
            _dirtyFlag = DirtyFlags.Clean;
        }
        
        private void tvClass_MouseDown(object sender, MouseEventArgs e)
        {
            TreeNode treeNode = tvClass.GetNodeAt(e.X, e.Y);
            if(treeNode!=null)
                tvClass.SelectedNode = treeNode;
        }

        private void InitializeTreeview()
        {
            tvClass.Nodes.Clear();
            _classesNode = new TreeNode("Business Entities");
            _classesNode.ContextMenuStrip = contextMenuEntities;
            tvClass.Nodes.Add(_classesNode);
        }


        private void TraverseLoadNodes(TreeNode rootNode)
        {
            DataNode dataNode = rootNode.Tag as DataNode;
            foreach (DataNode childNode in dataNode.ChildNodes)
            {
                TreeNode treeNode = new TreeNode(childNode.Caption);
                treeNode.Tag = childNode;
                treeNode.ContextMenuStrip = GetContextMenuByNodeType(childNode.NodeType);
                rootNode.Nodes.Add(treeNode);
            }

            foreach (TreeNode node in rootNode.Nodes)
            {
                TraverseLoadNodes(node);
            }
        }

        private void connectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            InitializeTreeview();
            DataModelContext.Initialize(Guid.Empty);
            foreach(EntityMetadata entityMetadata in DataModelWorkspace.Current.GetAllEntityMetadatas())
            {
                TreeNode treeNode = new TreeNode(entityMetadata.Caption);
                treeNode.Tag = entityMetadata;
                treeNode.ContextMenuStrip = contextMenuEntity;
                _classesNode.Nodes.Add(treeNode);
            }

            foreach (TreeNode node in _classesNode.Nodes)
            {
                TraverseLoadNodes(node);
            }
        }

        private void newEntityDefToolStripMenuItem_Click(object sender, EventArgs e)
        {
            NewNode((int)DataNodeTypes.Entity, "NewEntity");
        }

        private void newFieldToolStripMenuItem_Click(object sender, EventArgs e)
        {
            NewNode((int)DataNodeTypes.Field, "NewField");
        }

        private void openTreeItem_Click(object sender, EventArgs e)
        {
            curTreeNode.Expand();
            viewNode(curTreeNode = tvClass.SelectedNode);
        }

        private void tvClass_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (_dirtyFlag!= DirtyFlags.Clean && curTreeNode != tvClass.SelectedNode)
            {
                if (MessageBox.Show("The changes to the previous node will be lost.\nDo you want to save the previous node?", "Confirm",
                    MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    UpdateCurNode();
                }
                else
                {
                    RefreshCurNode();
                }
            }
            viewNode(curTreeNode = tvClass.SelectedNode);
        }

        private void viewNode(TreeNode node)
        {
            propertyGrid.SelectedObject = node.Tag;
        }

        private void onDelete(object sender, EventArgs e)
        {
            if (MessageBox.Show(string.Format("Are you sure to delete the node ({0}) ?", curTreeNode.Text), "Promp", MessageBoxButtons.YesNo) != DialogResult.Yes)
                return;
            if (_dirtyFlag == DirtyFlags.New)
            {
                _dirtyFlag = DirtyFlags.Clean;
                curTreeNode.Remove();
                buttonUpdate.Enabled = false;
                buttonRefresh.Enabled = false;
            }
            else
            {
                TreeNode parentTreeNode = curTreeNode.Parent;
                DataNode curDataNode = (DataNode)curTreeNode.Tag;
                MetadataManager.DeleteDataNode(curDataNode);
                if (curDataNode.ParentNode != null)
                    curDataNode.ParentNode.ChildNodes.Remove(curDataNode.Id);
                if (_dirtyFlag == DirtyFlags.Update)
                {
                    _dirtyFlag = DirtyFlags.Clean;
                    buttonUpdate.Enabled = false;
                    buttonRefresh.Enabled = false;
                }
                curTreeNode.Remove();
            }
        }

        private void PrintLine(string line)
        {
            outputResult.AppendLine(line);
        }

        private void TraverseNodesGetScripts(TreeNode rootNode)
        {
            DataNode dataNode = rootNode.Tag as DataNode;
            if (dataNode != null)
            {
                PrintLine(SqlEntityHelper<DataNode>.GenerateInsertScripts(dataNode));
                foreach (DataProperty property in dataNode.Properties)
                {
                    PrintLine(SqlEntityHelper<DataProperty>.GenerateInsertScripts(property));
                }
            }

            foreach (TreeNode node in rootNode.Nodes)
            {
                TraverseNodesGetScripts(node);
            }
        }

        protected string GetInsertScripts()
        {
            outputResult = new StringBuilder();
            foreach (TreeNode treeNode in _classesNode.Nodes)
            {
                TraverseNodesGetScripts(treeNode);
            }
            PrintLine("GO");
            return outputResult.ToString();
        }

        private void GenerateScripts(object sender, EventArgs e)
        {
            ShowScriptsForm form = new ShowScriptsForm(GetInsertScripts());
            form.ShowDialog();       
            //close file
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FileDialog dlg = new SaveFileDialog();
            dlg.Filter = "(*.sql)|*.sql";
            dlg.FileName = "New File";
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                System.IO.StreamWriter sw = null;
                try
                {
                    sw = new System.IO.StreamWriter(dlg.FileName, false);
                    sw.Write(GetInsertScripts());
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString(), "Error");
                }
                finally
                {
                    if (sw != null)
                        sw.Close();
                }
            }
            
        }

        private void propertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            DataNode dataNode = propertyGrid.SelectedObject as DataNode;
            if(string.Compare(e.ChangedItem.Label, "Caption", true)==0)
            {
                curTreeNode.Text = (string)e.ChangedItem.Value;
            }
            else if (string.Compare(e.ChangedItem.Label, "Name", true) == 0)
            {
                bool exists = false;
                foreach (TreeNode treeNode in curTreeNode.Parent.Nodes)
                {
                    DataNode tempNode = treeNode.Tag as DataNode;
                    if (tempNode != null && string.Compare(tempNode.Name, (string)e.ChangedItem.Value, true) == 0
                        &&!object.ReferenceEquals(tempNode, dataNode))
                    {
                        exists = true;
                        break;
                    }
                }
                if (exists == true)
                {
                    dataNode.Name = (string)e.OldValue;
                    MessageBox.Show(string.Format("The node with name {0} already exists!", (string)e.ChangedItem.Value));
                    propertyGrid.SelectedObject = dataNode;
                    return;
                }
            }
            if (_dirtyFlag != DirtyFlags.New)
                _dirtyFlag = DirtyFlags.Update;
            buttonUpdate.Enabled = true;
            buttonRefresh.Enabled = true;
        }

        private void RefreshCurNode()
        {
            if (curTreeNode.Tag != null)
            {
                if (_dirtyFlag == DirtyFlags.New)
                {
                    _dirtyFlag = DirtyFlags.Clean;
                    TreeNode parentTreeNode = curTreeNode.Parent;
                    curTreeNode.Remove();
                    tvClass.SelectedNode = parentTreeNode;
                }
                else
                {
                    DataNode dataNode = curTreeNode.Tag as DataNode;
                    DataNode parentNode = dataNode.ParentNode;
                    parentNode.ChildNodes.Remove(dataNode.Id);
                    dataNode = MetadataManager.GetDataNode(dataNode.Id);
                    parentNode.ChildNodes.Add(dataNode);
                    curTreeNode.Tag = dataNode;
                    curTreeNode.Text = dataNode.Caption;
                    propertyGrid.SelectedObject = dataNode;
                    _dirtyFlag = DirtyFlags.Clean;
                } 
                buttonUpdate.Enabled = false;
                buttonRefresh.Enabled = false;
            }
        }

        private void buttonRefresh_Click(object sender, EventArgs e)
        {
            RefreshCurNode();
        }

        private void UpdateCurNode()
        {
            if (curTreeNode.Tag != null)
            {
                TreeNode treeNode = curTreeNode;
                DataNode dataNode = curTreeNode.Tag as DataNode;
                if (_dirtyFlag == DirtyFlags.Update)
                {
                    MetadataManager.UpdateDataNode(dataNode);
                }
                else if(_dirtyFlag == DirtyFlags.New)
                {
                    Exception exception = null;
                    try
                    {
                        dataNode = MetadataManager.CreateDataNode(dataNode);
                        if (dataNode == null)
                            throw new Exception("Create fail");
                        curTreeNode.Tag = dataNode; //rebind the new datanode
                        DataNode parent = (DataNode)treeNode.Parent.Tag;
                        if (parent != null)
                            parent.ChildNodes.Add(dataNode);
                    }
                    catch(Exception e)
                    {
                        exception = e;
                        _dirtyFlag = DirtyFlags.Clean;
                        treeNode.Remove();
                    }
                    if (exception != null)
                    {
                        MessageBox.Show(exception.ToString(), "Fail");
                    }
                }
                _dirtyFlag = DirtyFlags.Clean;
                buttonUpdate.Enabled = false;
                buttonRefresh.Enabled = false;
            }
        }

        private void buttonUpdate_Click(object sender, EventArgs e)
        {
            UpdateCurNode();
        }

        private string GenerateNewNodeName(TreeNode parentTreeNode, string prefixName)
        {
            int i = 0;
            for (; i < parentTreeNode.Nodes.Count; i++)
            {
                DataNode dataNode = parentTreeNode.Nodes[i].Tag as DataNode;
                if (string.Compare(dataNode.Name, prefixName + (i + 1), true) == 0)
                    continue;
            }
            return prefixName + (i + 1);
        }

        private TreeNode NewNode(int nodeType, string prefixName)
        {
            if (_dirtyFlag != DirtyFlags.Clean)
            {
                if (MessageBox.Show("The changes to the previous node will be lost.\nDo you want to save the previous node?", "Confirm",
                    MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    UpdateCurNode();
                }
                else
                {
                    if (_dirtyFlag == DirtyFlags.New)
                        curTreeNode.Remove();
                    else
                        RefreshCurNode();
                }
            }
            string name = GenerateNewNodeName(curTreeNode, prefixName);
            DataNode parentDataNode = (DataNode)curTreeNode.Tag;
            DataNode newDataNode = MetadataManager.NewDataNode(nodeType);
            newDataNode.Initialize(
                Guid.NewGuid(),
                name,
                nodeType,
                parentDataNode == null ? null : (Guid?)parentDataNode.Id);        
            newDataNode.Caption = name;
            TreeNode newTreeNode = new TreeNode(name);
            newTreeNode.Tag = newDataNode;
            newTreeNode.ContextMenuStrip = GetContextMenuByNodeType(nodeType);
            curTreeNode.Nodes.Add(newTreeNode);
            _dirtyFlag = DirtyFlags.New;
            curTreeNode = newTreeNode;
            tvClass.SelectedNode = newTreeNode;
            buttonUpdate.Enabled = true;
            buttonRefresh.Enabled = true;
            return newTreeNode;
        }

        private ContextMenuStrip GetContextMenuByNodeType(int nodeType)
        {
            DataNodeTypes enumNodeType = (DataNodeTypes)nodeType;
            switch (enumNodeType)
            {
                case DataNodeTypes.Entity:
                    return contextMenuEntity;
                case DataNodeTypes.Field:
                    return contextMenuAttribute;
                default:
                    return null;
            }
        }
    }
}